package cn.topcodes.tcsf.codegen.utils;

import cn.topcodes.tcsf.codegen.domain.entity.Entity;
import cn.topcodes.tcsf.codegen.domain.entity.Field;
import cn.topcodes.tcsf.codegen.domain.entity.Program;
import com.j256.ormlite.dao.ForeignCollection;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Unicorn
 */
public class CodegenUtil {
    
    private static Configuration configuration;
    
    private static String templatePath = "template";   //  模板位置
    
    public static void genCode(Program program) throws FileNotFoundException, IOException, UnsupportedEncodingException, TemplateException {
        String codegenJavaPath = program.getFolderPath() + "/src/main/java";
        String codegenResourcePath = program.getFolderPath() + "/src/main/resources";
        String codegenJspPath = program.getFolderPath() + "/src/main/webapp/WEB-INF/views";
        ForeignCollection<Entity> entitys = program.getEntitys();
        for(Entity entity : entitys) {
            genCode(entity,program.getAuthor(),codegenJavaPath,codegenJspPath);
        }
        
        genInitSql(codegenResourcePath, entitys);
    }
    
    public static void genCode(Entity entity,String author, String codegenJavaPath, String codegenJspPath)
            throws UnsupportedEncodingException, FileNotFoundException, IOException, TemplateException {
        //创建一个合适的Configration对象  
        configuration = new Configuration();  
        configuration.setNumberFormat("#");
        configuration.setDirectoryForTemplateLoading(new File(templatePath));  
        configuration.setObjectWrapper(new DefaultObjectWrapper());  
        configuration.setDefaultEncoding("UTF-8"); 
        
        String entityClass = entity.getEntityClass();
        String voClass = entity.getVoClass();
        String formClass = entity.getFormClass();
        String displayName = entity.getDisplayName();
        String controllerUrl = entity.getControllerUrl();
        String pageFolder = entity.getPageFolder();
        
        ForeignCollection<Field> mFields = entity.getFields();
        List<Field> fields = new ArrayList<Field>();
        for(Field field : mFields) {
            fields.add(field);
        }
        
        //  开始生成
        genEntity(codegenJavaPath,entity.getTableName(),entityClass,fields,author, displayName);
        genVo(codegenJavaPath,voClass,fields,author, displayName);
        genForm(codegenJavaPath,formClass,fields,author, displayName);
        //  genEntity2VoConvertor(codegenJavaPath,voClass, entityClass, entity.getEntity2VoConvertorClass(), author,displayName);
        //  genForm2EntityConvertor(codegenJavaPath,formClass, entityClass, entity.getForm2EntityConvertorClass(), author,displayName);
        
        genMapper(codegenJavaPath, author, entity.getMapperClass(),entityClass,displayName);
        genService(codegenJavaPath,author,entity.getServiceClass(),entityClass,displayName);   
        genController(codegenJavaPath,pageFolder,entity.getControllerUrl(),entity.getControllerClass(),
                    entityClass, voClass, formClass, author, displayName);
        
        genListPage(codegenJspPath, displayName,pageFolder,controllerUrl,fields);
        genCreatePage(codegenJspPath, displayName,pageFolder,controllerUrl,fields);
        genModifyPage(codegenJspPath, displayName,pageFolder,controllerUrl,fields);
        genDetailPage(codegenJspPath, displayName,pageFolder,controllerUrl,fields);
        
    }
    
    /**
     * 生成实体
     * @param codegenJavaPath 
     * @param tableName 表名
     * @param entityClass   实体类全名
     * @param fields    字段列表
     * @param author 作者
     * @param displayName 显示名称
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genEntity(String codegenJavaPath, String tableName, String entityClass, List<Field> fields, String author, String displayName) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException { 
        String entityPackage = getPackageName(entityClass);
        String entityName = getClassName(entityClass);
                
        String parentFolderPath = codegenJavaPath + "/" + entityPackage.replace(".", "/");
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("entityPackage", entityPackage);
        paramMap.put("entityName", entityName);
        paramMap.put("tableName", tableName);
        paramMap.put("fields", fields);
        
        paramMap.put("displayName", displayName);
        paramMap.put("author", author);
        paramMap.put("date", DateUtil.formatCurrentDate());  

        genCodeByTemplate(paramMap,"Entity.java",parentFolderPath + "/" + entityName + ".java");
    }
    
    /**
     * 生成Mapper
     * @param codegenJavaPath   Java文件生成根路径
     * @param author    作者
     * @param mapperClass   Mapper类全名
     * @param entityClass   实体类全名
     * @param displayName   实体显示名
     * @throws MalformedTemplateNameException
     * @throws UnsupportedEncodingException
     * @throws FileNotFoundException
     * @throws TemplateException
     * @throws IOException 
     */
    public static void genMapper(String codegenJavaPath, String author, String mapperClass, String entityClass, String displayName) 
            throws MalformedTemplateNameException, UnsupportedEncodingException, FileNotFoundException, TemplateException, IOException {
        String entityName = getClassName(entityClass);
        
        String mapperPackage = getPackageName(mapperClass);
        String mapperName = getClassName(mapperClass);
        
        String parentFolderPath = codegenJavaPath + "/" + mapperPackage.replace(".", "/");
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();  
        paramMap.put("mapperPackage", mapperPackage);
        paramMap.put("mapperName", mapperName);
        paramMap.put("entityClass", entityClass);
        paramMap.put("entityName", entityName);
        
        paramMap.put("displayName", displayName);
        paramMap.put("author", author);
        paramMap.put("date", DateUtil.formatCurrentDate());

        genCodeByTemplate(paramMap,"Mapper.java",parentFolderPath + "/" + mapperName + ".java");
    }
    
    /**
     * 生成Service
     * @param codegenJavaPath   Java文件生成根路径
     * @param author    作者
     * @param serviceClass  Service类全名
     * @param entityClass   实体类全名
     * @param displayName   显示名称
     * @throws MalformedTemplateNameException
     * @throws UnsupportedEncodingException
     * @throws FileNotFoundException
     * @throws TemplateException
     * @throws IOException 
     */
    public static void genService(String codegenJavaPath, String author, String serviceClass, String entityClass, String displayName) 
            throws MalformedTemplateNameException, UnsupportedEncodingException, FileNotFoundException, TemplateException, IOException {
        String entityName = getClassName(entityClass);
        
        String servicePackage = getPackageName(serviceClass);
        String serviceName = getClassName(serviceClass);
        
        String parentFolderPath = codegenJavaPath + "/" + servicePackage.replace(".", "/");
        checkFolderPath(parentFolderPath);
          
        Map<String, Object> paramMap = new HashMap<>();  
        paramMap.put("servicePackage", servicePackage);  
        paramMap.put("serviceName", serviceName);  
        paramMap.put("entityClass", entityClass);  
        paramMap.put("entityName", entityName);  
        
        paramMap.put("displayName", displayName);
        paramMap.put("author", author);
        paramMap.put("date", DateUtil.formatCurrentDate());  

        genCodeByTemplate(paramMap,"Service.java",parentFolderPath + "/" + serviceName + ".java");
    }
    
    /**
     * 生成Vo
     * @param codegenJavaPath   Java文件生成根路径
     * @param voClass   vo类全名
     * @param fields    字段列表
     * @param author    作者
     * @param displayName   显示名称
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genVo(String codegenJavaPath, String voClass, List<Field> fields, String author, String displayName) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException {
        String voPackage = getPackageName(voClass);
        String voName = getClassName(voClass);
        
        String parentFolderPath = codegenJavaPath + "/" + voPackage.replace(".", "/");
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("voPackage", voPackage);
        paramMap.put("voName", voName);

        paramMap.put("fields", fields);
        
        paramMap.put("displayName", displayName);
        paramMap.put("author", author);
        paramMap.put("date", DateUtil.formatCurrentDate());  

        genCodeByTemplate(paramMap,"Vo.java",parentFolderPath + "/" + voName + ".java");
    }
    
    /**
     * 生成表单对象
     * @param codegenJavaPath      Java文件生成根路径
     * @param formClass     表单类全名
     * @param fields        字段列表
     * @param author        作者
     * @param displayName   显示名称
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genForm(String codegenJavaPath,String formClass, List<Field> fields, String author, String displayName) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException {
        String formPackage = getPackageName(formClass);
        String formName = getClassName(formClass);
        
        String parentFolderPath = codegenJavaPath + "/" + formPackage.replace(".", "/");
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        
        paramMap.put("formPackage", formPackage);
        paramMap.put("formName", formName);

        paramMap.put("fields", fields);
        
        paramMap.put("displayName", displayName);
        paramMap.put("author", author);
        paramMap.put("date", DateUtil.formatCurrentDate());   

        genCodeByTemplate(paramMap,"Form.java",parentFolderPath + "/" + formName + ".java");
    }
    
    /**
     * 生成表单实体转换器
     * @param codegenJavaPath     Java文件生成根路径
     * @param formClass     表单类全名
     * @param entityClass   实体类全名
     * @param form2EntityConvertorClass 转换器类全名
     * @param author        作者
     * @param displayName   显示名称
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genForm2EntityConvertor(String codegenJavaPath, String formClass, String entityClass,
            String form2EntityConvertorClass, String author, String displayName) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException {
        String entityName = getClassName(entityClass);
        String formName = getClassName(formClass);
        
        String form2EntityConvertorPackage = getPackageName(form2EntityConvertorClass);
        String form2EntityConvertorName = getClassName(form2EntityConvertorClass);
                
        String parentFolderPath = codegenJavaPath + "/" + form2EntityConvertorPackage.replace(".", "/");
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        
        paramMap.put("form2EntityConvertorPackage", form2EntityConvertorPackage);
        paramMap.put("form2EntityConvertorName", form2EntityConvertorName);
        
        paramMap.put("entityClass", entityClass);
        paramMap.put("entityName", entityName);
        
        paramMap.put("formClass", formClass);
        paramMap.put("formName", formName);
       
        paramMap.put("displayName", displayName);
        paramMap.put("author", author);
        paramMap.put("date", DateUtil.formatCurrentDate());  

        genCodeByTemplate(paramMap,"Form2EntityConvertor.java",parentFolderPath + "/" + entityName + "Form2EntityConvertor.java");
    }
    
    /**
     * 生成实体Vo转换器
     * @param codegenJavaPath   Java文件生成根路径
     * @param voClass   Vo类全名
     * @param entityClass   实体类全名
     * @param entity2VoConvertorClass 转换器类全名
     * @param author        作者
     * @param displayName   显示名称
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genEntity2VoConvertor(String codegenJavaPath,String voClass, String entityClass,
            String entity2VoConvertorClass, String author, String displayName) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException {
        String entity2VoConvertorPackage = getPackageName(entity2VoConvertorClass);
        String entity2VoConvertorName = getClassName(entity2VoConvertorClass);
        
        String voName = getClassName(voClass);
        String entityName = getClassName(entityClass);
        
        String parentFolderPath = codegenJavaPath + "/" + entity2VoConvertorPackage.replace(".", "/");
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("entity2VoConvertorPackage", entity2VoConvertorPackage);
        paramMap.put("entity2VoConvertorName", entity2VoConvertorName);
        
        paramMap.put("entityClass", entityClass);
        paramMap.put("entityName", entityName);
        
        paramMap.put("voClass", voClass);
        paramMap.put("voName", voName);
        
        paramMap.put("displayName", displayName);
        paramMap.put("author", author);
        paramMap.put("date", DateUtil.formatCurrentDate());     

        genCodeByTemplate(paramMap,"Entity2VoConvertor.java",parentFolderPath + "/" + entityName + "Entity2VoConvertor.java");
    }
    
    /**
     * 生成控制器
     * @param codegenJavaPath   Java文件生成根路径
     * @param pageFolder        jsp存放位置
     * @param controllerUrl     控制器URL
     * @param controllerClass   控制器类全名
     * @param entityClass   实体类全名
     * @param voClass   Vo类全名
     * @param formClass  表单类全名
     * @param author    作者
     * @param displayName   显示名称
     * @throws MalformedTemplateNameException
     * @throws UnsupportedEncodingException
     * @throws FileNotFoundException
     * @throws TemplateException
     * @throws IOException 
     */
    public static void genController(String codegenJavaPath,String pageFolder,String controllerUrl,String controllerClass, String entityClass, 
            String voClass, String formClass, String author, String displayName) 
            throws MalformedTemplateNameException, UnsupportedEncodingException, FileNotFoundException, TemplateException, IOException {
        String entityName = getClassName(entityClass);
        
        String controllerPackage = getPackageName(controllerClass);
        String controllerName = getClassName(controllerClass);
        
        String voName = getClassName(voClass);
        String formName = getClassName(formClass);
        
        String parentFolderPath = codegenJavaPath + "/" + controllerPackage.replace(".", "/");
        checkFolderPath(parentFolderPath);
          
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("controllerPackage", controllerPackage);
        paramMap.put("controllerName", controllerName);
        paramMap.put("entityClass", entityClass);
        paramMap.put("entityName", entityName);
        paramMap.put("voClass", voClass);
        paramMap.put("voName", voName);
        paramMap.put("formClass", formClass);
        paramMap.put("formName", formName);
        paramMap.put("controllerUrl", controllerUrl);
        paramMap.put("pageFolder", pageFolder);
        
        paramMap.put("displayName", displayName);
        paramMap.put("author", author);
        paramMap.put("date", DateUtil.formatCurrentDate());      

        genCodeByTemplate(paramMap,"Controller.java",parentFolderPath + "/" + controllerName + ".java");
    }
    
    /**
     * 生成列表页面
     * @param codegenJspPath    Jsp文件生成根路径
     * @param displayName   显示名称
     * @param pageFolder    页面存放位置
     * @param controllerUrl 控制器url
     * @param fields    字段列表
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genListPage(String codegenJspPath, String displayName,String pageFolder, String controllerUrl, List<Field> fields) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException { 
        
        String parentFolderPath = codegenJspPath + "/" + pageFolder;
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("displayName", displayName);
        paramMap.put("controllerUrl", controllerUrl);
        paramMap.put("fields", fields);
        boolean hasQueryField = false;
        for(Field field : fields) {
            if(field.isQueryField()) {
                hasQueryField = true;
                break;
            }
        }
        paramMap.put("hasQueryField", hasQueryField);
        
        genCodeByTemplate(paramMap,"index.jsp",parentFolderPath + "/" + "index.jsp");
    }
    
    /**
     * 生成创建页面
     * @param codegenJspPath    Jsp文件生成根路径
     * @param displayName   显示名称
     * @param pageFolder    页面存放位置
     * @param controllerUrl 控制器url
     * @param fields    字段列表
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genCreatePage(String codegenJspPath, String displayName,String pageFolder, String controllerUrl, List<Field> fields) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException { 
        
        String parentFolderPath = codegenJspPath + "/" + pageFolder;
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("displayName", displayName);
        paramMap.put("controllerUrl", controllerUrl);
        paramMap.put("fields", fields);

        
        genCodeByTemplate(paramMap,"create.jsp",parentFolderPath + "/" + "create.jsp");
    }
    
    /**
     * 生成编辑页面
     * @param codegenJspPath    Jsp文件生成根路径
     * @param displayName   显示名称
     * @param pageFolder    页面存放位置
     * @param controllerUrl 控制器url
     * @param fields    字段列表
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genModifyPage(String codegenJspPath,String displayName,String pageFolder, String controllerUrl, List<Field> fields) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException { 
        
        String parentFolderPath = codegenJspPath + "/" + pageFolder;
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("displayName", displayName);
        paramMap.put("controllerUrl", controllerUrl);
        paramMap.put("fields", fields);
        
        genCodeByTemplate(paramMap,"update.jsp",parentFolderPath + "/" + "update.jsp");
    }
    
    /**
     * 生成详情页面
     * @param codegenJspPath    Jsp文件生成根路径
     * @param displayName   显示名称
     * @param pageFolder    页面存放位置
     * @param controllerUrl 控制器url
     * @param fields    字段列表
     * @throws MalformedTemplateNameException
     * @throws ParseException
     * @throws IOException
     * @throws TemplateException 
     */
    public static void genDetailPage(String codegenJspPath,String displayName,String pageFolder, String controllerUrl, List<Field> fields) 
            throws MalformedTemplateNameException, ParseException, IOException, TemplateException { 
        
        String parentFolderPath = codegenJspPath + "/" + pageFolder;
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("displayName", displayName);
        paramMap.put("controllerUrl", controllerUrl);
        paramMap.put("fields", fields);
        
        genCodeByTemplate(paramMap,"detail.jsp",parentFolderPath + "/" + "detail.jsp");
    }
    
    
    //  获取包名
    private static String getPackageName(String clazz) {
        return clazz.substring(0, clazz.lastIndexOf("."));
    }
    
    //  获取类名
    private static String getClassName(String clazz) {
        return clazz.substring(clazz.lastIndexOf(".") + 1);
    }
    
    /**
     * 通过模板生成具体代码
     * @param paramMap  模板变量
     * @param templateFile  模板文件
     * @param targetPath    生成目标文件路径
     * @throws UnsupportedEncodingException
     * @throws TemplateException
     * @throws FileNotFoundException
     * @throws MalformedTemplateNameException
     * @throws IOException 
     */
    private static void genCodeByTemplate(Map<String, Object> paramMap, String templateFile, String targetPath) 
            throws UnsupportedEncodingException, TemplateException, FileNotFoundException, MalformedTemplateNameException, IOException {
        Template template = configuration.getTemplate(templateFile);
        Writer writer  = new OutputStreamWriter(new FileOutputStream(targetPath),"UTF-8");
        template.process(paramMap, writer);  
        writer.close();
    }

    /**
     * 检查文件夹是否存在，不存在则创建
     * @param path 
     */
    private static void checkFolderPath(String path) {
        File parentFolder = new File(path);
        if(!parentFolder.exists() || !parentFolder.isDirectory()) {
            parentFolder.mkdirs();
        }
    }

    private static void genInitSql(String codegenResourcePath, ForeignCollection<Entity> entitys) 
            throws TemplateException, FileNotFoundException, MalformedTemplateNameException, IOException {
        String parentFolderPath = codegenResourcePath;
        checkFolderPath(parentFolderPath);
        
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("entitys", entitys);
        
        genCodeByTemplate(paramMap,"init.sql",parentFolderPath + "/" + "init.sql");
    }
}
